package sliceutil

import (
	"errors"
	"fmt"
	"math"
	"reflect"
	"sort"
	"strings"
	"time"

	"gitee.com/haodreams/libs/easy"
)

// Ordering decides the order in which the specified data is sorted.
type Ordering int

func (o Ordering) String() string {
	return orderings[o]
}

// A runtime panic will occur if case-insensitive is used when not sorting by
// a string type.
const (
	Ascending Ordering = iota
	Descending
)

var orderings = []string{
	"asc",
	"desc",
}

// Recognized non-standard types
var (
	tTime = reflect.TypeOf(time.Time{})
)

// Sorter A reflecting sort.Interface adapter.
type Sorter struct {
	Slice    reflect.Value
	key      string
	Ordering Ordering
	itemType reflect.Type    // Type of items being sorted
	vals     []reflect.Value // Nested/child values that we're sorting by
	valKind  reflect.Kind
	valType  reflect.Type
}

// Sort the values in s.Slice by retrieving comparison items using
// s.Getter(s.Slice). A runtime panic will occur if s.Getter is not applicable
// to s.Slice, or if the values retrieved by s.Getter can't be compared, i.e.
// are unrecognized types.
func (s *Sorter) Sort() (err error) {
	if s.Slice.Len() < 2 {
		// Nothing to sort
		return
	}
	list := s.Slice

	isPtr := true
	v := list.Index(0)
	s.itemType = v.Type()

	t := reflect.Indirect(v).Type()
	if v.Kind() == reflect.Interface {
		t = reflect.TypeOf(v.Interface())
		v = reflect.ValueOf(v.Interface())
		isPtr = false
	}

	s.vals = valueSlice(list.Len())

	keyMap := map[string][]int{}
	easy.MapNameID("", keyMap, nil, t, true)
	ids, ok := keyMap[s.key]
	if !(ok && len(ids) > 0) {
		err = errors.New("value's name invalid")
		return
	}

	one, err := reflect.Indirect(v).FieldByIndexErr(ids)
	if err != nil {
		return
	}
	one = reflect.Indirect(one)

	//one := reflect.Indirect(reflect.Indirect(slice.Index(0)).FieldByName(s.key))
	//fmt.Println(one.Kind())

	if one.Kind() == reflect.Invalid {
		err = errors.New("value's type invalid")
		return
	}

	s.valType = one.Type()

	if isPtr {
		for i := range s.vals {
			s.vals[i] = reflect.New(s.valType).Elem()
			s.vals[i].Set(reflect.Indirect(reflect.Indirect(list.Index(i)).FieldByIndex(ids)))
		}
	} else {
		for i := range s.vals {
			s.vals[i] = reflect.New(s.valType).Elem()
			v := reflect.ValueOf(list.Index(i).Interface())
			s.vals[i].Set(reflect.Indirect(reflect.Indirect(v).FieldByIndex(ids)))
		}
	}

	//s.valType = one.Type()
	s.valKind = one.Kind()
	switch s.valKind {
	// If the value isn't a standard kind, find a known type to sort by
	default:
		switch s.valType {
		default:
			return fmt.Errorf("cann't sort by type %v", s.valType)
		case tTime:
			switch s.Ordering {
			default:
				return fmt.Errorf("invalid ordering %v for time.Time", s.Ordering)
			case Ascending:
				sort.Sort(timeAscending{s})
			case Descending:
				sort.Sort(timeDescending{s})
			}
		}
	// Strings
	case reflect.String:
		switch s.Ordering {
		default:
			panic(fmt.Sprintf("Invalid ordering %v for strings", s.Ordering))
		case Ascending:
			sort.Sort(stringAscending{s})
		case Descending:
			sort.Sort(stringDescending{s})
		}
	// Booleans
	case reflect.Bool:
		switch s.Ordering {
		default:
			panic(fmt.Sprintf("Invalid ordering %v for booleans", s.Ordering))
		case Ascending:
			sort.Sort(boolAscending{s})
		case Descending:
			sort.Sort(boolDescending{s})
		}
	// Ints
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		switch s.Ordering {
		default:
			panic(fmt.Sprintf("Invalid ordering %v for ints", s.Ordering))
		case Ascending:
			sort.Sort(intAscending{s})
		case Descending:
			sort.Sort(intDescending{s})
		}
	// Uints
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		switch s.Ordering {
		default:
			panic(fmt.Sprintf("Invalid ordering %v for uints", s.Ordering))
		case Ascending:
			sort.Sort(uintAscending{s})
		case Descending:
			sort.Sort(uintDescending{s})
		}
	// Floats
	case reflect.Float32, reflect.Float64:
		switch s.Ordering {
		default:
			panic(fmt.Sprintf("Invalid ordering %v for floats", s.Ordering))
		case Ascending:
			sort.Sort(floatAscending{s})
		case Descending:
			sort.Sort(floatDescending{s})
		}
	}
	return
}

// Len Returns the length of the slice being sorted.
func (s *Sorter) Len() int {
	return len(s.vals)
}

// Swap Swaps two indices in the slice being sorted.
func (s *Sorter) Swap(i, j int) {
	//值交换，key 也需要交换
	x := s.Slice.Index(i)
	y := s.Slice.Index(j)
	tmp := reflect.New(s.itemType).Elem()
	tmp.Set(x)
	x.Set(y)
	y.Set(tmp)

	//key 交换
	x = s.vals[i]
	y = s.vals[j]
	tmp = reflect.New(x.Type()).Elem()
	tmp.Set(x)
	x.Set(y)
	y.Set(tmp)

}

// *cough* typedef *cough*
type stringAscending struct{ *Sorter }
type stringDescending struct{ *Sorter }
type boolAscending struct{ *Sorter }
type boolDescending struct{ *Sorter }
type intAscending struct{ *Sorter }
type intDescending struct{ *Sorter }
type uintAscending struct{ *Sorter }
type uintDescending struct{ *Sorter }
type floatAscending struct{ *Sorter }
type floatDescending struct{ *Sorter }
type timeAscending struct{ *Sorter }
type timeDescending struct{ *Sorter }

//type reverser struct{ *Sorter }

func (s stringAscending) Less(i, j int) bool {
	return s.Sorter.vals[i].String() < s.Sorter.vals[j].String()
}

func (s stringDescending) Less(i, j int) bool {
	return s.Sorter.vals[i].String() > s.Sorter.vals[j].String()
}

func (s boolAscending) Less(i, j int) bool {
	return !s.Sorter.vals[i].Bool() && s.Sorter.vals[j].Bool()
}
func (s boolDescending) Less(i, j int) bool {
	return s.Sorter.vals[i].Bool() && !s.Sorter.vals[j].Bool()
}

func (s intAscending) Less(i, j int) bool   { return s.Sorter.vals[i].Int() < s.Sorter.vals[j].Int() }
func (s intDescending) Less(i, j int) bool  { return s.Sorter.vals[i].Int() > s.Sorter.vals[j].Int() }
func (s uintAscending) Less(i, j int) bool  { return s.Sorter.vals[i].Uint() < s.Sorter.vals[j].Uint() }
func (s uintDescending) Less(i, j int) bool { return s.Sorter.vals[i].Uint() > s.Sorter.vals[j].Uint() }

func (s floatAscending) Less(i, j int) bool {
	a := s.Sorter.vals[i].Float()
	b := s.Sorter.vals[j].Float()
	return a < b || math.IsNaN(a) && !math.IsNaN(b)
}

func (s floatDescending) Less(i, j int) bool {
	a := s.Sorter.vals[i].Float()
	b := s.Sorter.vals[j].Float()
	return a > b || !math.IsNaN(a) && math.IsNaN(b)
}

func (s timeAscending) Less(i, j int) bool {
	return s.Sorter.vals[i].Interface().(time.Time).Before(s.Sorter.vals[j].Interface().(time.Time))
}

func (s timeDescending) Less(i, j int) bool {
	return s.Sorter.vals[i].Interface().(time.Time).After(s.Sorter.vals[j].Interface().(time.Time))
}

// func (s reverser) Len() int {
// 	return s.Sorter.Slice.Len()
// }

// // Unused--only to satisfy sort.Interface
// func (s reverser) Less(i, j int) bool {
// 	return i < j
// }

// Asc Sort a slice in ascending order by a field name.
func Asc(slice interface{}, name string) (err error) {
	if name == "" {
		return nil
	}
	name = strings.ToLower(name)
	sorter := Sorter{}
	sorter.Slice = reflect.Indirect(reflect.ValueOf(slice))
	sorter.key = name
	//sorter.Getter = FieldGetter(name)
	sorter.Ordering = Ascending
	return sorter.Sort()
}

// Desc Sort a slice in descending order by a field name.
func Desc(slice interface{}, name string) (err error) {
	if name == "" {
		return nil
	}
	name = strings.ToLower(name)
	sorter := new(Sorter)
	sorter.Slice = reflect.Indirect(reflect.ValueOf(slice))
	sorter.key = name
	//sorter.Getter = FieldGetter(name)
	sorter.Ordering = Descending
	return sorter.Sort()
}

// OrderBy 再map中找到符合规则的数据
func OrderBy(slice interface{}, key string, order string) {
	// key := mp["sortKey"]
	// value := mp["sortOrder"]
	if order == "asc" {
		Asc(slice, key)
	} else if order == "desc" {
		Desc(slice, key)
	}
}

//DescMap 再map中找到符合规则的数据
